//===--- SILGenTopLevel.cpp - Top-level Code Emission ---------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "SILGenTopLevel.h"
#include "SILGenFunction.h"
#include "Scope.h"
#include "swift/AST/DiagnosticsSIL.h"

#define DEBUG_TYPE "silgen"

using namespace swift;
using namespace Lowering;

void SILGenModule::emitEntryPoint(SourceFile *SF, SILFunction *TopLevel) {

  auto EntryRef = SILDeclRef::getMainFileEntryPoint(SF);
  bool isAsyncTopLevel = false;
  if (SF->isAsyncContext()) {
    isAsyncTopLevel = true;
    auto asyncEntryRef = SILDeclRef::getAsyncMainFileEntryPoint(SF);
    auto *asyncTopLevel = getFunction(asyncEntryRef, ForDefinition);
    SILGenFunction(*this, *TopLevel, SF)
        .emitAsyncMainThreadStart(asyncEntryRef);
    TopLevel = asyncTopLevel;
    EntryRef = asyncEntryRef;
  }

  TopLevel->createProfiler(EntryRef);

  SILGenFunction TopLevelSGF(*this, *TopLevel, SF,
                             /* IsEmittingTopLevelCode */ true);
  TopLevelSGF.MagicFunctionName = SwiftModule->getName();
  auto moduleCleanupLoc = CleanupLocation::getModuleCleanupLocation();

  TopLevelSGF.prepareEpilog(llvm::None, true, moduleCleanupLoc);

  auto prologueLoc = RegularLocation::getModuleLocation();
  prologueLoc.markAsPrologue();
  if (SF->isAsyncContext()) {
    // emitAsyncMainThreadStart will create argc and argv.
    // Just set the main actor as the expected executor; we should
    // already be running on it.
    SILValue executor = TopLevelSGF.emitMainExecutor(prologueLoc);
    TopLevelSGF.ExpectedExecutor = TopLevelSGF.B.createOptionalSome(
        prologueLoc, executor, SILType::getOptionalType(executor->getType()));
  } else {
    // Create the argc and argv arguments.
    auto entry = TopLevelSGF.B.getInsertionBB();
    auto context = TopLevelSGF.getTypeExpansionContext();
    auto paramTypeIter =
        TopLevelSGF.F.getConventions().getParameterSILTypes(context).begin();

    entry->createFunctionArgument(*paramTypeIter);
    entry->createFunctionArgument(*std::next(paramTypeIter));
  }

  {
    Scope S(TopLevelSGF.Cleanups, moduleCleanupLoc);
    SILGenTopLevel(TopLevelSGF).visitSourceFile(SF);
  }

  // Unregister the top-level function emitter.
  TopLevelSGF.stopEmittingTopLevelCode();

  // Write out the epilog.
  auto moduleLoc = RegularLocation::getModuleLocation();
  moduleLoc.markAutoGenerated();
  auto returnInfo = TopLevelSGF.emitEpilogBB(moduleLoc);
  auto returnLoc = returnInfo.second;
  returnLoc.markAutoGenerated();

  SILFunction *exitFunc = nullptr;

  SILType returnType;
  if (isAsyncTopLevel) {
    FuncDecl *exitFuncDecl = getExit();
    assert(exitFuncDecl && "Failed to find exit function declaration");
    exitFunc = getFunction(
        SILDeclRef(exitFuncDecl, SILDeclRef::Kind::Func, /*isForeign*/ true),
        NotForDefinition);
    SILFunctionType &funcType =
        *exitFunc->getLoweredType().getAs<SILFunctionType>();
    returnType = SILType::getPrimitiveObjectType(
        funcType.getParameters().front().getInterfaceType());
  } else {
    returnType = TopLevelSGF.F.getConventions().getSingleSILResultType(
        TopLevelSGF.getTypeExpansionContext());
  }

  auto emitTopLevelReturnValue = [&](unsigned value) -> SILValue {
    // Create an integer literal for the value.
    auto litType = SILType::getBuiltinIntegerType(32, getASTContext());
    SILValue retValue =
        TopLevelSGF.B.createIntegerLiteral(moduleLoc, litType, value);

    // Wrap that in a struct if necessary.
    if (litType != returnType) {
      retValue = TopLevelSGF.B.createStruct(moduleLoc, returnType, retValue);
    }
    return retValue;
  };

  // Fallthrough should signal a normal exit by returning 0.
  SILValue returnValue;
  if (TopLevelSGF.B.hasValidInsertionPoint())
    returnValue = emitTopLevelReturnValue(0);

  // Handle the implicit rethrow block.
  auto rethrowBB = TopLevelSGF.ThrowDest.getBlock();
  TopLevelSGF.ThrowDest = JumpDest::invalid();

  // If the rethrow block wasn't actually used, just remove it.
  if (rethrowBB->pred_empty()) {
    TopLevelSGF.eraseBasicBlock(rethrowBB);

    // Otherwise, we need to produce a unified return block.
  } else {
    auto returnBB = TopLevelSGF.createBasicBlock();
    if (TopLevelSGF.B.hasValidInsertionPoint())
      TopLevelSGF.B.createBranch(returnLoc, returnBB, returnValue);
    returnValue = returnBB->createPhiArgument(returnType, OwnershipKind::Owned);
    TopLevelSGF.B.emitBlock(returnBB);

    // Emit the rethrow block.
    SILGenSavedInsertionPoint savedIP(TopLevelSGF, rethrowBB,
                                      FunctionSection::Postmatter);

    // Log the error.
    SILValue error = rethrowBB->getArgument(0);
    TopLevelSGF.B.createBuiltin(moduleLoc,
                                getASTContext().getIdentifier("errorInMain"),
                                Types.getEmptyTupleType(), {}, {error});

    // Then end the lifetime of the error.
    //
    // We do this to appease the ownership verifier. We do not care about
    // actually destroying the value since we are going to immediately exit,
    // so this saves us a slight bit of code-size since end_lifetime is
    // stripped out after ownership is removed.
    TopLevelSGF.B.createEndLifetime(moduleLoc, error);

    // Signal an abnormal exit by returning 1.
    TopLevelSGF.Cleanups.emitCleanupsForReturn(CleanupLocation(moduleLoc),
                                               IsForUnwind);
    TopLevelSGF.B.createBranch(returnLoc, returnBB, emitTopLevelReturnValue(1));
  }

  // Return.
  if (TopLevelSGF.B.hasValidInsertionPoint()) {

    if (isAsyncTopLevel) {
      SILValue exitCall = TopLevelSGF.B.createFunctionRef(moduleLoc, exitFunc);
      TopLevelSGF.B.createApply(moduleLoc, exitCall, {}, {returnValue});
      TopLevelSGF.B.createUnreachable(moduleLoc);
    } else {
      TopLevelSGF.B.createReturn(returnLoc, returnValue);
    }
  }

  // Okay, we're done emitting the top-level function; destroy the
  // emitter and verify the result.
  SILFunction &toplevel = TopLevelSGF.getFunction();

  LLVM_DEBUG(llvm::dbgs() << "lowered toplevel sil:\n";
             toplevel.print(llvm::dbgs()));
  toplevel.verifyIncompleteOSSA();
  emitLazyConformancesForFunction(&toplevel);
}

void SILGenModule::emitEntryPoint(SourceFile *SF) {
  assert(!M.lookUpFunction(getASTContext().getEntryPointFunctionName()) &&
         "already emitted toplevel?!");

  auto mainEntryRef = SILDeclRef::getMainFileEntryPoint(SF);
  SILFunction *TopLevel = getFunction(mainEntryRef, ForDefinition);
  TopLevel->setBare(IsBare);
  emitEntryPoint(SF, TopLevel);
}

void SILGenFunction::emitMarkFunctionEscapeForTopLevelCodeGlobals(
    SILLocation Loc, CaptureInfo CaptureInfo) {

  llvm::SmallVector<SILValue, 4> Captures;

  for (auto Capture : CaptureInfo.getCaptures()) {
    // Decls captured by value don't escape.
    auto It = VarLocs.find(Capture.getDecl());
    if (It == VarLocs.end() || !It->getSecond().value->getType().isAddress())
      continue;

    Captures.push_back(It->second.value);
  }

  if (!Captures.empty())
    B.createMarkFunctionEscape(Loc, Captures);
}

/// Emit a `mark_function_escape_instruction` into `SGF` if `AFD` captures an
/// uninitialized global variable
static void emitMarkFunctionEscape(SILGenFunction &SGF,
                                   AbstractFunctionDecl *AFD) {
  if (AFD->getDeclContext()->isLocalContext())
    return;
  auto CaptureInfo = AFD->getCaptureInfo();
  SGF.emitMarkFunctionEscapeForTopLevelCodeGlobals(AFD, std::move(CaptureInfo));
}

SILGenTopLevel::SILGenTopLevel(SILGenFunction &SGF) : SGF(SGF) {}

void SILGenTopLevel::visitSourceFile(SourceFile *SF) {

  for (auto *D : SF->getTopLevelDecls()) {
    D->visitAuxiliaryDecls([&](Decl *AuxiliaryDecl) { visit(AuxiliaryDecl); });
    visit(D);
  }

  if (auto *SynthesizedFile = SF->getSynthesizedFile()) {
    for (auto *D : SynthesizedFile->getTopLevelDecls()) {
      if (isa<ExtensionDecl>(D)) {
        visit(D);
      }
    }
  }

  for (Decl *D : SF->getHoistedDecls()) {
    visit(D);
  }

  for (TypeDecl *TD : SF->LocalTypeDecls) {
    if (TD->getDeclContext()->getInnermostSkippedFunctionContext())
      continue;
    visit(TD);
  }
}

void SILGenTopLevel::visitNominalTypeDecl(NominalTypeDecl *NTD) {
  TypeVisitor(SGF).emit(NTD);
}

void SILGenTopLevel::visitExtensionDecl(ExtensionDecl *ED) {
  ExtensionVisitor(SGF).emit(ED);
}

void SILGenTopLevel::visitAbstractFunctionDecl(AbstractFunctionDecl *AFD) {
  emitMarkFunctionEscape(SGF, AFD);
}

void SILGenTopLevel::visitAbstractStorageDecl(AbstractStorageDecl *ASD) {
  ASD->visitEmittedAccessors(
      [this](AccessorDecl *Accessor) { visitAbstractFunctionDecl(Accessor); });
}

void SILGenTopLevel::visitTopLevelCodeDecl(TopLevelCodeDecl *TD) {

  SGF.emitProfilerIncrement(TD->getBody());

  DebugScope DS(SGF, CleanupLocation(TD));

  for (auto &ESD : TD->getBody()->getElements()) {
    if (!SGF.B.hasValidInsertionPoint()) {
      if (auto *S = ESD.dyn_cast<Stmt *>()) {
        if (S->isImplicit())
          continue;
      } else if (auto *E = ESD.dyn_cast<Expr *>()) {
        if (E->isImplicit())
          continue;
      }

      SGF.SGM.diagnose(ESD.getStartLoc(), diag::unreachable_code);
      // There's no point in trying to emit anything else.
      return;
    }

    if (auto *S = ESD.dyn_cast<Stmt *>()) {
      SGF.emitStmt(S);
    } else if (auto *E = ESD.dyn_cast<Expr *>()) {
      SGF.emitIgnoredExpr(E);
    } else {
      SGF.visit(ESD.get<Decl *>());
    }
  }
}

SILGenTopLevel::TypeVisitor::TypeVisitor(SILGenFunction &SGF) : SGF(SGF) {}

void SILGenTopLevel::TypeVisitor::emit(IterableDeclContext *Ctx) {
  for (auto *Member : Ctx->getMembersForLowering()) {
    visit(Member);
  }
}

void SILGenTopLevel::TypeVisitor::visitPatternBindingDecl(
    PatternBindingDecl *PD) {
  for (auto i : range(PD->getNumPatternEntries())) {
    if (!PD->getExecutableInit(i) || PD->isStatic())
      continue;
    auto *Var = PD->getAnchoringVarDecl(i);
    if (Var->getDeclContext()->isLocalContext())
      continue;
    auto CaptureInfo = PD->getCaptureInfo(i);

    // If this is a stored property initializer inside a type at global scope,
    // it may close over a global variable. If we're emitting top-level code,
    // then emit a "mark_function_escape" that lists the captured global
    // variables so that definite initialization can reason about this
    // escape point.
    SGF.emitMarkFunctionEscapeForTopLevelCodeGlobals(Var,
                                                     std::move(CaptureInfo));
  }
}

void SILGenTopLevel::TypeVisitor::visitNominalTypeDecl(NominalTypeDecl *NTD) {
  TypeVisitor(SGF).emit(NTD);
}

void SILGenTopLevel::TypeVisitor::visitAbstractFunctionDecl(
    AbstractFunctionDecl *AFD) {
  emitMarkFunctionEscape(SGF, AFD);
}

void SILGenTopLevel::TypeVisitor::visitAbstractStorageDecl(
    AbstractStorageDecl *ASD) {
  ASD->visitEmittedAccessors(
      [this](AccessorDecl *Accessor) { visitAbstractFunctionDecl(Accessor); });
}

SILGenTopLevel::ExtensionVisitor::ExtensionVisitor(SILGenFunction &SGF)
    : TypeVisitor(SGF) {}

void SILGenTopLevel::ExtensionVisitor::visitPatternBindingDecl(
    PatternBindingDecl *PD) {
  auto *Ctx = PD->getDeclContext();
  if (isa<ExtensionDecl>(Ctx) &&
      cast<ExtensionDecl>(Ctx)->isObjCImplementation()) {
    TypeVisitor::visitPatternBindingDecl(PD);
  }
}
